/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.querydsl.support;

import com.querydsl.sql.Configuration;
import com.querydsl.sql.SQLTemplates;
import com.querydsl.sql.types.*;
import net.apexes.commons.lang.Enume;
import net.apexes.commons.lang.EnumeRegistry;
import net.apexes.commons.querydsl.types.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
public class QuerydslEnumeSupport {

    private final EnumeRegistry enumeRegistry;
    private final List<TypeWrap> wrapList;

    @SuppressWarnings("unchecked")
    public QuerydslEnumeSupport(EnumeRegistry enumeRegistry) {
        this.enumeRegistry = enumeRegistry;
        this.wrapList = new ArrayList<>();

        for (EnumeRegistry.EnumeInfo<? extends Enume<?>> info : enumeRegistry.getEnumeInfos()) {
            Class<? extends Enume<?>> enumeClass = info.getEnumeClass();
            Class<? extends Comparable<?>> valueClass = Enume.valueClass(enumeClass);
            if (valueClass == String.class) {
                Class<Enume<String>> classType = (Class<Enume<String>>) enumeClass;
                wrapList.add(new TypeWrap(new EnumeByStringValueType<>(classType), info.getTableColumns()));
            } else if (valueClass == Integer.class) {
                Class<Enume<Integer>> classType = (Class<Enume<Integer>>) enumeClass;
                wrapList.add(new TypeWrap(new EnumeByIntegerValueType<>(classType), info.getTableColumns()));
            }
        }
    }

    public Collection<Class<? extends Enume<?>>> getEnumeClasses() {
        return enumeRegistry.getEnumeClasses();
    }

    public Collection<Type<?>> getEnumeTypes() {
        List<Type<?>> enumeTypes = new ArrayList<>();
        for (TypeWrap wrap : wrapList) {
            enumeTypes.add(wrap.enumeType);
        }
        return Collections.unmodifiableCollection(enumeTypes);
    }

    public Configuration register(Configuration configuration, List<String> tables) {
        for (TypeWrap wrap : wrapList) {
            for (EnumeRegistry.TableColumn tc : wrap.tableColumns) {
                for (String column : tc.getColumns()) {
                    if (tc.getTable() == null) {
                        for (String table : tables) {
                            configuration.register(table, column, wrap.enumeType);
                        }
                    } else {
                        configuration.register(tc.getTable(), column, wrap.enumeType);
                    }
                }
            }
        }
        return configuration;
    }

    public Configuration simpleConfiguration(SQLTemplates templates, List<String> tables) {
        return register(simpleConfiguration(templates), tables);
    }

    public static Configuration simpleConfiguration(SQLTemplates templates) {
        Configuration configuration = new Configuration(templates);
        configuration.register(new BlobByteArrayType());
        configuration.register(new JSR310LocalDateTimeType());
        configuration.register(new JSR310LocalDateType());
        configuration.register(new JSR310LocalTimeType());
        return configuration;
    }

    public static Configuration utilDateConfiguration(SQLTemplates templates) {
        Configuration configuration = new Configuration(templates);
        configuration.register(new BlobByteArrayType());
        configuration.register(new UtilDate2DateType());
        configuration.register(new UtilDateType());
        return configuration;
    }

    private static class TypeWrap {
        final EnumeType<?> enumeType;
        final List<EnumeRegistry.TableColumn> tableColumns;

        TypeWrap(EnumeType<?> enumeType, List<EnumeRegistry.TableColumn> tableColumns) {
            this.enumeType = enumeType;
            this.tableColumns = tableColumns;
        }
    }

}
